/* eslint-disable no-constant-condition */
import * as  THREE from "three";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { BvhNode } from "./BvhNode";

/**
 * 生成假数据，构建BVH，显示BVH的边框
 */
class BvhTreeImpl {
    private animationBind = this.animation.bind(this);
    private onPointerMoveBind = this.onPointerMove.bind(this);
    private control!: OrbitControls;
    private renderer!: THREE.WebGLRenderer;
    private camera!: THREE.Camera;
    private scene!: THREE.Scene;

    bvhTree!: BvhNode;
    start() {
        const objs = this.getRandomMesh(16);
        console.log("objs = ");
        console.log(objs);
        this.bvhTree = new BvhNode().buildTree(objs);
        console.log("tree = ");
        console.log(this.bvhTree);
        this.show(this.bvhTree);

    }
    private getRandomMesh(meshCount = 20): THREE.Mesh[] {
        const xRange = [-100, 100];
        const yRange = [-100, 100];
        const zRange = [-100, 100];
        const normalLen = (xRange[1] - xRange[0]) / meshCount * 4;
        const objects: THREE.Mesh[] = [];
        const mat = new THREE.MeshNormalMaterial();
        for (let index = 0; index < meshCount; index++) {
            const xR = Math.random() * (xRange[1] - xRange[0]) + xRange[0];
            const yR = Math.random() * (yRange[1] - yRange[0]) + yRange[0];
            const zR = Math.random() * (zRange[1] - zRange[0]) + zRange[0];
            const xl = Math.random() * normalLen;
            const yl = Math.random() * normalLen;
            const zl = Math.random() * normalLen;
            const geo = new THREE.BoxBufferGeometry(xl, yl, zl, 1, 1, 1);
            const m = new THREE.Mesh(geo, mat);
            m.position.set(xR, yR, zR);
            m.name = index.toString();
            objects.push(m);
        }
        return objects;
    }
    /**
     * 显示物体和边界框
     * @param tree
     */
    private show(tree: BvhNode) {
        this.scene = new THREE.Scene();
        this.scene.add(new THREE.AmbientLight(new THREE.Color(), 1));
        this.scene.add(...tree.objects);
        this.bbToScene(tree);

        this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10000);
        this.camera.position.z = 240;
        this.renderer = new THREE.WebGLRenderer({
            antialias: true,
        });
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setAnimationLoop(this.animationBind);

        this.control = new OrbitControls(this.camera, this.renderer.domElement);

        document.body.appendChild(this.renderer.domElement);
        this.renderer.domElement.addEventListener('pointermove', this.onPointerMoveBind);
    }
    /**
     * 边界框加入场景
     * @param tree
     */
    private bbToScene(tree: BvhNode, color = new THREE.Color(0xffffff)) {
        tree.computeBoundingBox();
        const box3Helper = new THREE.Box3Helper(tree.boundingBox, new THREE.Color(color));
        this.scene.add(box3Helper);
        const newColor = new THREE.Color(0xff0000 * Math.random() + 0x00ff00 * Math.random() + 0x0000ff * Math.random());
        if (tree.lChild && tree.rChild && tree.lChild.objects.length > 1 && tree.rChild.objects.length > 1) {
            this.bbToScene(tree.lChild, newColor);
            this.bbToScene(tree.rChild, newColor);
        }
    }
    private onPointerMove(event: any) {
        this.pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
        this.pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;
    }
    private intersection!: THREE.Intersection<THREE.Object3D<THREE.Event>>[];
    private raycaster: THREE.Raycaster = new THREE.Raycaster();
    private pointer: THREE.Vector2 = new THREE.Vector2();
    private animation(time: number) {
        this.control.update();
        this.renderer.render(this.scene, this.camera);

        this.raycaster.setFromCamera(this.pointer, this.camera);
        this.intersection = this.bvhTree.getIntersection(this.raycaster);
        if (this.intersection && this.intersection.length > 0) {
            console.log(this.intersection
                .sort((a, b) => { return a.distance - b.distance; })
                .map(r => { return r.object.name; })
            );
        }
    }
}

//======================================================================
const impl = new BvhTreeImpl();
impl.start();
